﻿using System.IO;
using System.IO.Ports;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml.Serialization;
using Zhaoxi.Communication.Base;
using Zhaoxi.Configuration.Entities;

namespace Zhaoxi.Configuration
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");

            //ProtocolTest();
            //ConfigFileTest();
            Communication();

            Console.ReadLine();
        }

        static void ProtocolTest()
        {
            // 一对一、一对多
            // ModbusRTU  
            SerialPort serialPort = new SerialPort("COM1");
            serialPort.Open();
            Modbus.Device.ModbusSerialMaster slave =
                Modbus.Device.ModbusSerialMaster.CreateRtu(serialPort);
            Task.Run(() =>
            {
                while (true)
                {
                    var result = slave.ReadHoldingRegisters(1, 0, 10);
                    result = slave.ReadHoldingRegisters(2, 0, 10);
                    slave.ReadCoils(1, 0, 10);
                    slave.ReadInputs(1, 0, 10);
                    slave.ReadInputRegisters(1, 0, 10);
                }
            });
            // COM2-》ModbusRTU
            serialPort = new SerialPort("COM2");
            serialPort.Open();
            slave = Modbus.Device.ModbusSerialMaster.CreateRtu(serialPort);
            Task.Run(() =>
            {
                while (true)
                {
                    var result = slave.ReadHoldingRegisters(1, 0, 10);
                    result = slave.ReadHoldingRegisters(2, 0, 10);
                    slave.ReadCoils(1, 0, 10);
                    slave.ReadInputs(1, 0, 10);
                    slave.ReadInputRegisters(1, 0, 10);
                }
            });


            // TCP  点对点   一对一（特殊情况不考虑）
            // 多个TCP设备（设备少的话，每个设备开个线程，设备多的话，线程开多的情况，占用CPU）
            // ModbusRTU
            TcpClient tcpClient = new TcpClient();
            tcpClient.Connect("127.0.0.1", 502);
            Modbus.Device.ModbusIpMaster ipMaster = Modbus.Device.ModbusIpMaster.CreateIp(tcpClient);
            ushort[] values = ipMaster.ReadHoldingRegisters(1, 0, 1);


            tcpClient = new TcpClient();
            tcpClient.Connect("127.0.0.2", 503);
            ipMaster = Modbus.Device.ModbusIpMaster.CreateIp(tcpClient);
            values = ipMaster.ReadHoldingRegisters(1, 0, 1);



            // S7
            S7.Net.Plc plc = new S7.Net.Plc(S7.Net.CpuType.S71200, "192.168.2.1", 102, 0, 0);
            plc.Open();
            plc.Read(S7.Net.DataType.DataBlock, 1, 100, S7.Net.VarType.Word, 5);



            // MC
            MCProtocol.Mitsubishi.McProtocolTcp mcProtocolTcp = new MCProtocol.Mitsubishi.McProtocolTcp();
            mcProtocolTcp.HostName = "192.169.1.100";
            mcProtocolTcp.PortNumber = 6000;
            mcProtocolTcp.CommandFrame = MCProtocol.Mitsubishi.McFrame.MC3E;
            mcProtocolTcp.Open();
            mcProtocolTcp.ReadDeviceBlock(MCProtocol.Mitsubishi.PlcDeviceType.D, 100, 5);

            // OPCUA


        }

        /// 观察基本信息
        /// ModbusRTU：
        ///     通信参数：COM1、波特率、数据位、校验位、停止位、读取超时时长。。。。。。。
        ///     变量参数：需要从设备中读取的信息：从站地址、功能区分、起始地址、长度、数据（写入逻辑）
        ///     
        /// ModbusTCP：
        ///     通信参数：IP、端口号、超时时间、缓存大小。。。（针对TCP对象的参数）
        ///     变量参数：从站地址、功能区分、起始地址、长度、数据（写入逻辑）
        ///     
        /// S7：
        ///     通信参数：IP、端口号（S7netplus默认102端口）、超时时间、缓存大小。。。。CPU类型（S7netplus）、机架号、插槽号
        ///     变量参数：存储区类型、DB编码、起始字节位置地址、值类型、长度、位地址
        ///     
        /// MC3E:
        ///     通信参数：IP、端口号、帧格式
        ///     变量参数：存储区类型、起始地址、长度


        // 一台设备：通信方式、协议、变量地址（地址、数据类型、偏移量、系数）
        // 1、以设备在单元，进行相关数据的存储    设备-》通信协议。。。。。。。
        // 2、以协议为单元，进行相关数据的存储    协议-》设备（KepServer   OPC服务器）（）


        // 用什么存？
        // 先将配置文件信息读取到C#对象中，通信进候从对象中获取
        // 应用启动的时候做初始化
        static void ConfigFileTest()
        {
            // 配置文件可以在运行目录
            // 硬盘任意位置
            {
                string file_path = Environment.CurrentDirectory + "/config/config.json";
                // 读
                // 
                string json_str = System.IO.File.ReadAllText(file_path);
                var obj = System.Text.Json.JsonSerializer.Deserialize<List<ConfigEntity>>(json_str);
                //obj[0].Name
                //obj[1].ID

                // 写

                obj.Add(new ConfigEntity { Name = "DDDD", ID = "1004" });
                obj[2].Name = "动态编辑";

                json_str = System.Text.Json.JsonSerializer.Serialize(obj);
                System.IO.File.WriteAllText(file_path, json_str, Encoding.UTF8);
            }


            // XML文件处理    序列化    
            // C#  序列  对象-》字符串（XML格式）  XmlSerializer
            // XmlDocument  完全操作XML节点
            // 注意：1、起始标签是哪个，进行设置，如果按照根节点处理，那就不用指定
            //      2、标签与C#对象间的名称匹配

            {
                string file_path = Environment.CurrentDirectory + "/config/config.xml";
                XmlSerializer serializer =
                    new XmlSerializer(typeof(List<ConfigEntity>), new XmlRootAttribute("workspace"));
                // 加载文件
                // 为什么要用using 指定一个作用域，走出括号后，自动释放变量
                using (var stream = new FileStream(file_path, FileMode.Open))
                {
                    var obj = serializer.Deserialize(stream);
                }

                List<ConfigEntity> datas = new List<ConfigEntity>();
                // 这里将对象序列化成XML字符串，然后定入到对应文件里去
                //serializer.Serialize(stream, datas);

            }
        }

        static void Communication()
        {
            // 第一步获取配置数据
            string file_path = Environment.CurrentDirectory + "/config/config.json";
            // 读
            // 
            string json_str = System.IO.File.ReadAllText(file_path);
            List<ConfigEntity> devices =
                System.Text.Json.JsonSerializer.Deserialize<List<ConfigEntity>>(json_str);


            // 根据配置信息，创建通信通过
            // 3个设备
            /// 根据这些信息进行通信的创建
            /// 
            foreach (var device in devices)
            {
                {
                    //if (device.Protocol == "ModbusRTU")
                    //{
                    //    SerialPort serialPort = new SerialPort("COM1");
                    //    serialPort.Open();
                    //    Modbus.Device.ModbusSerialMaster slave =
                    //        Modbus.Device.ModbusSerialMaster.CreateRtu(serialPort);
                    //}
                    //else if (device.Protocol == "ModbusTCP")
                    //{
                    //    TcpClient tcpClient = new TcpClient();
                    //    tcpClient.Connect("127.0.0.1", 502);
                    //    Modbus.Device.ModbusIpMaster ipMaster = Modbus.Device.ModbusIpMaster.CreateIp(tcpClient);
                    //}
                    //.....
                }
                // device.Protocol
                // 类必须在当前的程序集中？
                // 根据协议类的字符串的名称，进行反射获取对象
                // 好处：
                string[] ps = device.Protocol.Split(';');
                var assembly = Assembly.LoadFrom(ps[0] + ".dll");
                Type type = assembly.GetType(ps[1]);
                CommBase comm = (CommBase)Activator.CreateInstance(type);

                // 进行相关的通信创建
                comm.Connect(device.CommParams.ToArray());// 面向对象的多态

                // 
                Task.Run(async () =>
                {
                    //这里需要一个开关来控制结束线程
                    while (true)
                    {
                        comm.StartRead(device.Variables.ToArray());

                        Console.WriteLine(
                            String.Join(",", device.Variables.Select(v => v.Value.ToString()))
                            );

                        Console.WriteLine(
                            String.Join(";", device.Variables.Select(v => v.WarningMessage))
                            );

                        await Task.Delay(1000);
                    }
                });
            }


        }
    }
}
